#include <stdlib.h>
#include <string.h>
#include "cm_os.h"
#include "cm_mem.h"
#include "cm_fs.h"
#include "app_log.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lua_binding.h"
#include "lua_test_entry.h"
#include "lua_scheduler.h"
#include "lua_module_uart.h"
#include "lua_module_http.h"

#define LUA_TASK_STACK_SIZE (8 * 1024)
osThreadId_t lua_task_handle = NULL;
osMessageQueueId_t lua_event_queue = NULL;

extern lua_uart_cb_t g_uart_cbs[];
extern int ntp_callback_ref;
extern int net_urc_ref;

static void *lua_custom_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
    (void)ud;  (void)osize;
    if (nsize == 0) {
        cm_free(ptr);
        return NULL;
    } else {
        return cm_realloc(ptr, nsize);
    }
}

osStatus_t lua_event_post(const lua_event_t *event) {
    return osMessageQueuePut(lua_event_queue, event, 0U, 0U);
}

static void run_lua_logic(void *param)
{
    lua_State *L = lua_newstate(lua_custom_alloc, NULL);
    if (L == NULL) {
        app_log_printf("[E] Cannot create Lua state: not enough memory");
        return;
    }

    luaL_openlibs(L);
    luaopen_ml307c_libs(L);

    // Initialize the scheduler
    lua_scheduler_init(L);

    const char* filepath = "/main.lua";
    app_log_printf("[I] Attempting to load and run %s", filepath);

    // Load main.lua as the first task, using our custom file reading logic
    int32_t size = cm_fs_filesize(filepath);
    if (size > 0) {
        int32_t fd_main = cm_fs_open(filepath, CM_FS_RB);
        if (fd_main > 0) {
            char *buffer = (char *)malloc(size);
            if (buffer) {
                cm_fs_read(fd_main, buffer, size);
                if (luaL_loadbuffer(L, buffer, size, filepath) == LUA_OK) {
                    lua_scheduler_new_task(L);
                } else {
                    app_log_printf("[E] Failed to load main.lua: %s", lua_tostring(L, -1));
                    lua_pop(L, 1);
                }
                free(buffer);
            }
            cm_fs_close(fd_main);
        } else {
            app_log_printf("[E] Failed to open %s for reading.", filepath);
        }
    } else if (size == 0) {
        app_log_printf("[W] %s is empty. Lua engine is idle.", filepath);
    } else { // size < 0
         app_log_printf("[W] %s not found. Lua engine is idle.", filepath);
    }


    app_log_printf("[I] --- Starting Lua Scheduler and Event Loop ---");

    for (;;)
    {
        // Run one iteration of the Lua task scheduler
        lua_scheduler_run();

        // Check for and process one event from the C->Lua event queue
        lua_event_t event;
        if (osMessageQueueGet(lua_event_queue, &event, NULL, 0U) == osOK)
        {
            lua_getglobal(L, "sys");
            if (!lua_istable(L, -1)) {
                lua_pop(L, 1);
                continue; // Should not happen
            }
            lua_getfield(L, -1, "task");
            lua_remove(L, -2); // Remove sys table

            int nargs = 0;

            switch (event.id)
            {
                case LUA_EVENT_TIMER:
                {
                    lua_rawgeti(L, LUA_REGISTRYINDEX, event.data.ival);
                    nargs = 1;
                    break;
                }
                case LUA_EVENT_UART_RX:
                {
                    int uart_id = event.data.ival;
                    if (uart_id >= 0 && uart_id < UART_MAX_COUNT && g_uart_cbs[uart_id].rx_ref != LUA_NOREF) {
                        lua_rawgeti(L, LUA_REGISTRYINDEX, g_uart_cbs[uart_id].rx_ref);
                        lua_pushinteger(L, uart_id);
                        nargs = 2;
                    }
                    break;
                }
                case LUA_EVENT_NET_REG_STATUS:
                {
                    if (net_urc_ref != LUA_NOREF) {
                        lua_rawgeti(L, LUA_REGISTRYINDEX, net_urc_ref);
                        lua_pushinteger(L, event.data.ival);
                        nargs = 2;
                    }
                    break;
                }
                case LUA_EVENT_NTP_SYNC_RESULT:
                {
                    if (ntp_callback_ref != LUA_NOREF) {
                        ntp_result_t *res = (ntp_result_t *)event.data.pval;
                        lua_rawgeti(L, LUA_REGISTRYINDEX, ntp_callback_ref);
                        lua_pushboolean(L, res->is_ok);
                        lua_pushstring(L, res->time_str);
                        nargs = 3;
                        free(res);
                    }
                    break;
                }
                case LUA_EVENT_HTTP_RSP_DONE:
                case LUA_EVENT_HTTP_RSP_ERROR:
                case LUA_EVENT_HTTP_PROGRESS:
                {
                    http_event_data_t *http_data = (http_event_data_t*)event.data.pval;
                    lua_rawgeti(L, LUA_REGISTRYINDEX, http_data->client_ref);
                    lua_http_client_t *client = (lua_http_client_t *)lua_touserdata(L, -1);
                    lua_pop(L, 1);
                    if (client) {
                        if (event.id == LUA_EVENT_HTTP_RSP_DONE && client->on_done_ref != LUA_NOREF) {
                            lua_rawgeti(L, LUA_REGISTRYINDEX, client->on_done_ref);
                            lua_pushinteger(L, http_data->status_code);
                            nargs = 2;
                            if (client->file_handle == 0) { // Not a download
                                if (http_data->data) {
                                    lua_pushlstring(L, http_data->data, http_data->data_len);
                                } else {
                                    lua_pushstring(L, "");
                                }
                                nargs++;
                            }
                        } else if (event.id == LUA_EVENT_HTTP_RSP_ERROR && client->on_error_ref != LUA_NOREF) {
                            lua_rawgeti(L, LUA_REGISTRYINDEX, client->on_error_ref);
                            lua_pushinteger(L, http_data->status_code);
                            nargs = 2;
                        } else if (event.id == LUA_EVENT_HTTP_PROGRESS && client->on_progress_ref != LUA_NOREF) {
                            lua_rawgeti(L, LUA_REGISTRYINDEX, client->on_progress_ref);
                            lua_pushinteger(L, http_data->received_len);
                            lua_pushinteger(L, http_data->total_len);
                            nargs = 3;
                        }
                    }

                    if (event.id == LUA_EVENT_HTTP_RSP_DONE || event.id == LUA_EVENT_HTTP_RSP_ERROR) {
                        if (client) cleanup_client_resources(client);
                        if (http_data->data) free(http_data->data);
                    }
                    free(http_data);
                    break;
                }
                // TODO: Handle MQTT events
                default:
                    break;
            }

            if (nargs > 0) {
                if (lua_pcall(L, nargs, 0, 0) != LUA_OK) {
                    app_log_printf("[E] Failed to schedule task for event %d: %s", event.id, lua_tostring(L, -1));
                    lua_pop(L, 1);
                }
            } else {
                 lua_pop(L, 1); // Pop sys.task
            }
        }

        // Yield to other RTOS tasks
        osDelay(1);
    }

    // This part is no longer reachable, but kept for completeness
    lua_close(L);
}

void lua_test_entry_start(void)
{
    if (lua_task_handle != NULL)
        return;

    lua_event_queue = osMessageQueueNew(32, sizeof(lua_event_t), NULL);
    if (lua_event_queue == NULL) {
        return;
    }

    const osThreadAttr_t lua_task_attr = {
        .name = "lua_task",
        .stack_size = LUA_TASK_STACK_SIZE,
        .priority = (osPriority_t) osPriorityNormal,
    };
    lua_task_handle = osThreadNew(run_lua_logic, NULL, &lua_task_attr);
}

void cm_opencpu_entry(void)
{
    app_log_init(); // 初始化日志，请自行封装

    // 启动Lua任务
    lua_test_entry_start();
}
